/*
 * Copyright information
 */

#include "viewer-file.h"

#ifdef DERIVABLE
//for derivable class(结构体稳定)
/* Private structure definition. */
typedef struct {
    GObject *input_stream;;
    /* stuff */
    gchar *filename;
    guint zoom_level;
    gdouble autosave_frequency;
} ViewerFilePrivate;

#if GLIB_VERSION_MAX_ALLOWED  < (G_ENCODE_VERSION (2, 58))
    #define VIEWER_FILE_GET_PRIVATE(o) G_TYPE_INSTANCE_GET_PRIVATE((o), VIEWER_TYPE_FILE, ViewerFilePrivate)
#else
    #define VIEWER_FILE_GET_PRIVATE(o) ((ViewerFilePrivate*)viewer_file_get_instance_private(o))
#endif //GLIB_VERSION_MAX_ALLOWED

#else

struct _ViewerFile {
    GObject parent_instance;

    /* Other members, including private data. */
    GObject *input_stream;;
    gchar *filename;
    guint zoom_level;
    gdouble autosave_frequency;
    //ViewerFilePrivate *priv; #一般可省略
};
#endif //IS DERIVABLE

//object property
enum
{
    PROP_FILENAME = 1, //0保留
    PROP_ZOOM_LEVEL,
    N_PROPERTIES
    //for INF_EDITABLE
    ,
    PROP_AUTOSAVE_FREQUENCY = N_PROPERTIES,
    N_ALL_PROPERTIES
};

static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };

//object signal
enum {
    CHANGED, //注意这个值不是信号ID, 而是信号ID的索引
    LAST_SIGNAL
};

//可选用于存储信号ID, 方便引用, 使用信号ID比直接使用信号名称更高效
static guint file_signals[LAST_SIGNAL] = { 0 };


#if defined(INF_EDITABLE) || defined(INF_LOSSY)
#include "viewer-editable.h"
static void viewer_file_editable_interface_init (ViewerEditableInterface *iface);
#endif

#ifdef INF_LOSSY
#include "viewer-editable-lossy.h"
static void viewer_file_editable_lossy_interface_init (ViewerEditableLossyInterface *iface);
#endif

G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT,
#ifdef DERIVABLE
                         G_ADD_PRIVATE (ViewerFile)
#endif
#if defined(INF_EDITABLE) || defined(INF_LOSSY)
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_file_editable_interface_init)
#endif
#ifdef INF_LOSSY
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE_LOSSY,
                                                viewer_file_editable_lossy_interface_init)
#endif
)


/* interface implementation */

#if defined(INF_EDITABLE) || defined(INF_LOSSY)
static void
viewer_file_editable_save (ViewerEditable  *self,
                           const gchar *name)
{
    ViewerFile *vf = VIEWER_FILE(self); //接口转主对象

    g_object_set(G_OBJECT(vf), "filename", name, NULL);

    g_print ("File implementation of editable interface save method, file:%s.\n",
           name);
}

static void
viewer_file_editable_undo (ViewerEditable *self,
                           guint       n_steps)
{
    ViewerFile *vf = VIEWER_FILE(self); 
    gchar *filename = NULL;
    g_object_get(G_OBJECT(vf), "filename", &filename, NULL);
    g_print ("File implementation of editable interface undo method, file:%s.\n",
           filename);
}

static void
viewer_file_editable_redo (ViewerEditable *self,
                           guint       n_steps)
{
    ViewerFile *vf = VIEWER_FILE(self); 
    gchar *filename = NULL;
    g_object_get(G_OBJECT(vf), "filename", &filename, NULL);
    g_print ("File implementation of editable interface redo method, file:%s.\n",
            filename);
}

static void
viewer_file_editable_interface_init (ViewerEditableInterface *iface)
{
    g_print("calling %s\n", __func__);
    iface->save = viewer_file_editable_save;
    iface->undo = viewer_file_editable_undo;
    iface->redo = viewer_file_editable_redo;
}

#endif //INF_EDITABLE || INF_LOSSY

#ifdef INF_LOSSY

static void viewer_file_editable_lossy_compress(ViewerEditableLossy *self) {
    ViewerFile *vf = VIEWER_FILE (self);
    gchar *filename = NULL;

    g_object_get(G_OBJECT(vf), "filename", &filename, NULL);

    g_print ("File implementation of editable-lossy interface compress method, file:%s.\n",
            filename);
}

static void viewer_file_editable_lossy_interface_init(
    ViewerEditableLossyInterface *iface) {
    g_print("calling %s\n", __func__);
    iface->compress = viewer_file_editable_lossy_compress;
}

#endif //INF_LOSSY


#ifdef DERIVABLE
static void
viewer_file_real_open (ViewerFile  *self,
                  const gchar     *file) {
    g_print("calling %s\n", __func__);
    VIEWER_FILE_GET_PRIVATE(self)->filename = g_strdup(file);
    
}
static void
viewer_file_real_close (ViewerFile  *self,
                  GError     **error) {

  g_print("calling %s\n", __func__);

}

static gboolean
viewer_file_real_can_memory_map (ViewerFile *self)
{
  /* As an example, always return false. Or, potentially return true if the
   * file is local. */
  g_print("calling %s\n", __func__);
  return FALSE;
}
 
/* this accessor function is static: it is not exported outside of this file. */
static gboolean 
viewer_file_can_memory_map (ViewerFile *self) {
  g_print("calling %s\n", __func__);
  return VIEWER_FILE_GET_CLASS (self)->can_memory_map (self);
}

#endif //DERIVABLE

static void
viewer_file_constructed (GObject *obj)
{
    g_print("calling %s\n", __func__);
  /* update the object state depending on constructor properties */

  /* Always chain up to the parent constructed function to complete object
   * initialisation. */
  G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}

static void viewer_file_dispose(GObject *obj) {
    g_print("calling %s\n", __func__);
    ViewerFile *vf = VIEWER_FILE(obj); 

#ifdef DERIVABLE
    GObject **input_stream = &VIEWER_FILE_GET_PRIVATE(vf)->input_stream;
#else
    GObject **input_stream = &vf->input_stream;
#endif 
    g_clear_object(input_stream);
    G_OBJECT_CLASS(viewer_file_parent_class)->dispose(obj);
}

static void viewer_file_finalize(GObject *obj) {
    g_print("calling %s\n", __func__);
    ViewerFile *vf = VIEWER_FILE(obj);
    gchar *filename = NULL;

#ifdef DERIVABLE
    filename = VIEWER_FILE_GET_PRIVATE(vf)->filename;
#else
    filename = vf->filename;
#endif

    g_free(filename);
    G_OBJECT_CLASS(viewer_file_parent_class)->finalize(obj);
}

static void
viewer_file_set_property (GObject      *object,
                          guint         property_id,
                          const GValue *value,
                          GParamSpec   *pspec)
{
    ViewerFile *vf = VIEWER_FILE (object);

#ifdef DERIVABLE
    gchar **filename = &VIEWER_FILE_GET_PRIVATE(vf)->filename;
    guint *zoom_level = &VIEWER_FILE_GET_PRIVATE(vf)->zoom_level;
    gdouble *autosave_frequency =  &VIEWER_FILE_GET_PRIVATE(vf)->autosave_frequency;
#else
    gchar **filename = &vf->filename;
    guint *zoom_level = &vf->zoom_level;
    gdouble *autosave_frequency = &vf->autosave_frequency;
#endif 
    
    g_print("calling %s\n", __func__);

    switch (property_id) {
    case PROP_FILENAME:
      g_free (*filename);
      *filename = g_value_dup_string (value);
      break;

    case PROP_ZOOM_LEVEL:
      *zoom_level = g_value_get_uint (value);
      break;
    case PROP_AUTOSAVE_FREQUENCY:
      *autosave_frequency = g_value_get_double (value);
      break;

    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
viewer_file_get_property (GObject    *object,
                          guint       property_id,
                          GValue     *value,
                          GParamSpec *pspec)
{
    g_print("calling %s\n", __func__);
    ViewerFile *vf = VIEWER_FILE (object);

#ifdef DERIVABLE
    gchar *filename = VIEWER_FILE_GET_PRIVATE(vf)->filename;
    guint zoom_level = VIEWER_FILE_GET_PRIVATE(vf)->zoom_level;
    gdouble autosave_frequency =  VIEWER_FILE_GET_PRIVATE(vf)->autosave_frequency;
#else
    gchar *filename = vf->filename;
    guint zoom_level = vf->zoom_level;
    gdouble autosave_frequency =  vf->autosave_frequency;
#endif 

    switch (property_id) {
    case PROP_FILENAME:
        g_value_set_string (value, filename);
        break;

    case PROP_ZOOM_LEVEL:
        g_value_set_uint (value, zoom_level);
        break;
    
    case PROP_AUTOSAVE_FREQUENCY:
        g_value_set_double(value, autosave_frequency);
        break;

    default:
        /* We don't have any other property... */
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        break;
    }
}



static void
viewer_file_init (ViewerFile *self)
{
  /* Instance variable initialisation code. */
    g_print("calling %s\n", __func__);

#ifdef DERIVABLE
    GObject **input_stream = &VIEWER_FILE_GET_PRIVATE(self)->input_stream;
#else
    GObject **input_stream = &self->input_stream;
#endif 

    *input_stream = NULL;
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
    /* Instance variable initialisation code. */
    g_print("calling %s\n", __func__);
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    //必须在属性安装之前赋值
    object_class->get_property = viewer_file_get_property;
    object_class->set_property = viewer_file_set_property;
    obj_properties[PROP_FILENAME] =
        g_param_spec_string ("filename",
                             "Filename",
                             "Name of the file to load and display from.",
                             NULL  /* default value */,
                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
    
    obj_properties[PROP_ZOOM_LEVEL] =
        g_param_spec_uint ("zoom-level",
                           "Zoom level",
                           "Zoom level to view the file at.",
                           0  /* minimum value */,
                           10 /* maximum value */,
                           2  /* default value */,
                           G_PARAM_READWRITE);
    g_object_class_install_properties (object_class,
                                       N_PROPERTIES,
                                       obj_properties);
#if defined(INF_EDITABLE) || defined(INF_LOSSY)
    //覆盖接口、父类属性
    g_object_class_override_property (object_class,
                                      PROP_AUTOSAVE_FREQUENCY, 
                                      "autosave-frequency");
#else
    g_object_class_install_property (
        object_class,
        PROP_AUTOSAVE_FREQUENCY,
        g_param_spec_double ("autosave-frequency",
                             "Autosave frequency",
                             "Frequency (in per-seconds) to autosave backups of the editable content at. "
                             "Or zero to disable autosaves.",
                             0.0,  /* minimum */
                             G_MAXDOUBLE,  /* maximum */
                             0.0,  /* default */
                             G_PARAM_READWRITE));
#endif //INF_EDITABLE PROP
    
    //注册信号
    file_signals[CHANGED] =
        g_signal_new (
            "changed", //signal
            G_TYPE_FROM_CLASS (object_class), //GType
            // singal flags 
            G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
            0, /*class_offset*/
            NULL /* accumulator */,
            NULL /* accumulator data */,
            NULL /* C marshaller */,
            G_TYPE_NONE /* return_type */,
            1     /* n_params */,
            G_TYPE_STRING /* param_types */);

    object_class->dispose = viewer_file_dispose;
    object_class->finalize = viewer_file_finalize;

#ifdef DERIVABLE
    klass->open = viewer_file_real_open;
    klass->close = viewer_file_real_close;
    klass->can_memory_map = viewer_file_real_can_memory_map;
#endif
}


/* implementation in the source file */
void
viewer_file_open (ViewerFile  *self,
                  const gchar *file)
{
    ViewerFileClass *klass;
    g_print("calling %s\n", __func__);
    
    g_return_if_fail (VIEWER_IS_FILE (self));
    //g_return_if_fail (error == NULL || *error == NULL);


#ifdef DERIVABLE
    klass = VIEWER_FILE_GET_CLASS (self);
    g_return_if_fail (klass->open != NULL);
    klass->open (self, file);
    if (viewer_file_can_memory_map (self)) {
        /* Load the file using memory mapped I/O. */
    } else {
      /* Fall back to trying to load the file using streaming I/O… */
    }
#else
    self->filename = g_strdup(file);
#endif


}

void
viewer_file_close (ViewerFile  *self,
                  GError     **error)
{
  ViewerFileClass *klass;
  g_print("calling %s\n", __func__);

  g_return_if_fail (VIEWER_IS_FILE (self));
  //g_return_if_fail (error == NULL || *error == NULL);


#ifdef DERIVABLE
  klass = VIEWER_FILE_GET_CLASS (self);
  g_return_if_fail (klass->close != NULL);
  klass->close (self, error);
#endif

}


void
viewer_file_write (ViewerFile   *self,
                   const gchar *buffer,
                   gsize         size) {
    g_return_if_fail (VIEWER_IS_FILE (self));
    g_return_if_fail (buffer != NULL || size == 0);

    /* First write data. */

    /* Then, notify user of data written. */
    g_signal_emit (self, file_signals[CHANGED], 0 /* details */, buffer);
}
